#include "spi_flash.h"

#if SPI_FLASH_CHIP == W25Q128
#include "config.h"


static int spi_flash_initialised = 0;

static uint32_t SPI_FLASH_ReadDeviceID(void)
{
	uint32_t Temp = 0;
	SPIFlash_CSn_Clr;
	/* Send "RDID " instruction */
	SPI_ReadWrite(SPI_PORT_W25X, W25X_DeviceID);
	SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);
	SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);
	SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);
  
	/* Read a byte from the FLASH */
	Temp = SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);
	SPIFlash_CSn_Set;
	return Temp;
}

 /**
  * @brief  读取FLASH ID
  * @param 	无
  * @retval FLASH ID
  */

//uint32_t Temp1000 = 0;
static uint32_t SPI_FLASH_ReadID(void)
{
	uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
	SPIFlash_CSn_Clr;
	/* 发送JEDEC指令，读取ID */
	SPI_ReadWrite(SPI_PORT_W25X, W25X_JedecDeviceID);
	Temp0 = SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);/* 读取一个字节数据 */
	Temp1 = SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);
	Temp2 = SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);
	SPIFlash_CSn_Set;
	/*把数据组合起来，作为函数的返回值*/
	Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
//	Temp1000=Temp;
	return Temp;
}

/**
  * @brief  向FLASH发送 写使能 命令
  * @param  none
  * @retval none
  */
static void SPI_FLASH_WriteEnable(void)
{
	SPIFlash_CSn_Clr;						/* 通讯开始：CS低 */
	SPI_ReadWrite(SPI_PORT_W25X, W25X_WriteEnable); 	/* 发送写使能命令*/
	SPIFlash_CSn_Set;						/*通讯结束：CS高 */
}
 /**
  * @brief  等待WIP(BUSY)标志被置0，即等待到FLASH内部数据写入完毕
  * @param  none
  * @retval none
  */
static void SPI_FLASH_WaitForWriteEnd(void)
{
    uint16_t FLASH_Status;

    SPIFlash_CSn_Clr;

    SPI_ReadWrite(SPI_PORT_W25X, W25X_ReadStatusReg_1);
    
    /* Loop as long as the memory is busy with a write cycle */
    do
    {
        /* Send a dummy byte to generate the clock needed by the FLASH
        and put the value of the status register in FLASH_Status variable */
        FLASH_Status = SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);
    }
    while ((FLASH_Status & WIP_Flag) == 0x01); /* Write in progress */

    SPIFlash_CSn_Set;

}
/**
  * @brief  擦除FLASH扇区
  * @param  SectorAddr：要擦除的扇区地址
  * @retval 无
  */
static void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
	SPI_FLASH_WriteEnable();/* 发送FLASH写使能命令 */
	/* 擦除扇区 */
	SPIFlash_CSn_Clr;						/* 通讯开始：CS低 */
	SPI_ReadWrite(SPI_PORT_W25X, W25X_SectorErase);	/* 发送扇区擦除指令*/
	SPI_ReadWrite(SPI_PORT_W25X, (SectorAddr & 0xFF0000) >> 16);/*发送擦除扇区地址的高位*/
	/* 发送擦除扇区地址的中位 */
	SPI_ReadWrite(SPI_PORT_W25X, (SectorAddr & 0xFF00) >> 8);
	/* 发送擦除扇区地址的低位 */
	SPI_ReadWrite(SPI_PORT_W25X, SectorAddr & 0xFF);
	SPIFlash_CSn_Set;						/*通讯结束：CS高 */
	/* 等待擦除完毕*/
    SPI_FLASH_WaitForWriteEnd();
}

/**
  * @brief  对FLASH按页写入数据，调用本函数写入数据前需要先擦除扇区
  * @param	pBuffer，要写入数据的指针
  * @param WriteAddr，写入地址
  * @param  NumByteToWrite，写入数据长度，必须小于等于SPI_FLASH_PerWritePageSize
  * @retval 无
  */
static void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
	/* 发送FLASH写使能命令 */
	SPI_FLASH_WriteEnable();

	SPIFlash_CSn_Clr;						/* 通讯开始：CS低 */
	/* 写页写指令*/
	SPI_ReadWrite(SPI_PORT_W25X, W25X_PageProgram);
	/*发送写地址的高位*/
	SPI_ReadWrite(SPI_PORT_W25X, (WriteAddr & 0xFF0000) >> 16);
	/*发送写地址的中位*/
	SPI_ReadWrite(SPI_PORT_W25X, (WriteAddr & 0xFF00) >> 8);
	/*发送写地址的低位*/
	SPI_ReadWrite(SPI_PORT_W25X, WriteAddr & 0xFF);

	/* 写入数据*/
	while (NumByteToWrite--)
	{
		SPI_ReadWrite(SPI_PORT_W25X, *pBuffer);/* 发送当前要写入的字节数据 */
		pBuffer++;/* 指向下一字节数据 */
	}
  
    SPIFlash_CSn_Set;						/*通讯结束：CS高 */

    SPI_FLASH_WaitForWriteEnd();
}

 /**
  * @brief  对FLASH写入数据，调用本函数写入数据前需要先擦除扇区
  * @param	pBuffer，要写入数据的指针
  * @param  WriteAddr，写入地址
  * @param  NumByteToWrite，写入数据长度
  * @retval 无
  */
static void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
	uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

	Addr = WriteAddr % SPI_FLASH_PageSize;	/*mod运算求余，若writeAddr是SPI_FLASH_PageSize整数倍，运算结果Addr值为0*/
	count = SPI_FLASH_PageSize - Addr;		/*差count个数据值，刚好可以对齐到页地址*/
	NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;	/*计算出要写多少整数页*/
	NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;	/*mod运算求余，计算出剩余不满一页的字节数*/

	if (Addr == 0) 							/* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
	{
		if (NumOfPage == 0) 				/* NumByteToWrite < SPI_FLASH_PageSize */
		{
			SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
		}
		else /* NumByteToWrite > SPI_FLASH_PageSize */
		{
			while (NumOfPage--)/*先把整数页都写了*/
			{
			SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
			WriteAddr +=  SPI_FLASH_PageSize;
			pBuffer += SPI_FLASH_PageSize;
		}
			
			/*若有多余的不满一页的数据，把它写完*/
			SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
		}
	}
	/* 若地址与 SPI_FLASH_PageSize 不对齐  */
	else 
	{
		/* NumByteToWrite < SPI_FLASH_PageSize */
	if (NumOfPage == 0) 
	{
			/*当前页剩余的count个位置比NumOfSingle小，写不完*/
	  if (NumOfSingle > count) 
	  {
		temp = NumOfSingle - count;
				
				/*先写满当前页*/
		SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
		WriteAddr +=  count;
		pBuffer += count;
				
				/*再写剩余的数据*/
		SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
	  }
	  else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
	  {				
		SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
	  }
	}
	else /* NumByteToWrite > SPI_FLASH_PageSize */
	{
			/*地址不对齐多出的count分开处理，不加入这个运算*/
	  NumByteToWrite -= count;
	  NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
	  NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

	  SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
	  WriteAddr +=  count;
	  pBuffer += count;
			
			/*把整数页都写了*/
	  while (NumOfPage--)
	  {
		SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
		WriteAddr +=  SPI_FLASH_PageSize;
		pBuffer += SPI_FLASH_PageSize;
	  }
			/*若有多余的不满一页的数据，把它写完*/
	  if (NumOfSingle != 0)
	  {
		SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
	  }
	}
	}
}
/**
  * @brief  读取FLASH数据
  * @param 	pBuffer，存储读出数据的指针
  * @param   ReadAddr，读取地址
  * @param   NumByteToRead，读取数据长度
  * @retval 无
  */
__weak void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
	SPIFlash_CSn_Clr;						/* 通讯开始：CS低 */
	SPI_ReadWrite(SPI_PORT_W25X, W25X_ReadData);		/* 发送 读 指令 */

	SPI_ReadWrite(SPI_PORT_W25X, (ReadAddr & 0xFF0000) >> 16);	/* 发送 读 地址高位 */
	SPI_ReadWrite(SPI_PORT_W25X, (ReadAddr& 0xFF00) >> 8);		/* 发送 读 地址中位 */
	SPI_ReadWrite(SPI_PORT_W25X, ReadAddr & 0xFF);				/* 发送 读 地址低位 */
  
	while (NumByteToRead--)					/* 读取数据 */
	{
		*pBuffer = SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);/* 读取一个字节*/
		pBuffer++;							/* 指向下一个字节缓冲区 */
	}
  
	SPIFlash_CSn_Set;						/*通讯结束：CS高 */
}


 /**
  * @brief  快速读取FLASH数据
  * @param 	pBuffer，存储读出数据的指针
  * @param   ReadAddr，读取地址
  * @param   NumByteToRead，读取数据长度
  * @retval 无
  */
__weak void SPI_FLASH_BufferFastRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
	SPIFlash_CSn_Clr;						/* 通讯开始：CS低 */
	SPI_ReadWrite(SPI_PORT_W25X, W25X_FastReadData);		/* 发送 读 指令 */

	SPI_ReadWrite(SPI_PORT_W25X, (ReadAddr & 0xFF0000) >> 16);	/* 发送 读 地址高位 */
	SPI_ReadWrite(SPI_PORT_W25X, (ReadAddr& 0xFF00) >> 8);		/* 发送 读 地址中位 */
	SPI_ReadWrite(SPI_PORT_W25X, ReadAddr & 0xFF);				/* 发送 读 地址低位 */
    SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);
    
	while (NumByteToRead--)					/* 读取数据 */
	{
		*pBuffer = SPI_ReadWrite(SPI_PORT_W25X, Dummy_Byte);/* 读取一个字节*/
		pBuffer++;							/* 指向下一个字节缓冲区 */
	}
  
	SPIFlash_CSn_Set;						/*通讯结束：CS高 */
}

 
//进入掉电模式
__weak void SPI_Flash_PowerDown(void)   
{ 
	SPIFlash_CSn_Clr;						/* 通讯开始：CS低 */
	SPI_ReadWrite(SPI_PORT_W25X, W25X_PowerDown); 	/* 发送 掉电 命令 */
	SPIFlash_CSn_Set;						/*通讯结束：CS高 */
}

__weak void SPI_Flash_WAKEUP(void)    //唤醒SPI_FLASH
{
  /*选择 FLASH: CS 低 */
  SPIFlash_CSn_Clr;

  /* 发上 上电 命令 */
  SPI_ReadWrite(SPI_PORT_W25X, W25X_ReleasePowerDown);

  /* 停止信号 FLASH: CS 高 */
  SPIFlash_CSn_Set;                   //等待TRES1
}

/**
 * Initialises a spi_flash interface
 *
 * @param[in]  spi_flash  the interface which should be initialised
 *
 * @return  0 : on success,  otherwise is error
 */
int32_t hal_spi_flash_init(spi_flash_dev_t *spi_flash)
{
	SPI_InitStructure SPI_initStruct;

	GPIO_Init(SPI_NSS_PORT, SPI_NSS_PIN,  1, 0, 0);				// 输出，接CS

	SPIFlash_CSn_Set;

	PORT_Init(SPI_SCLK_PORT, SPI_SCLK_PIN, FUNMUX1_SPI1_SCLK, 0);
	PORT_Init(SPI_MOSI_PORT, SPI_MOSI_PIN, FUNMUX0_SPI1_MOSI, 0);
	PORT_Init(SPI_MISO_PORT, SPI_MISO_PIN, FUNMUX1_SPI1_MISO, 1);
	
	SPI_initStruct.clkDiv = SPI_CLKDIV_4;
	SPI_initStruct.FrameFormat = SPI_FORMAT_SPI;
	SPI_initStruct.SampleEdge = SPI_FIRST_EDGE;
	SPI_initStruct.IdleLevel = SPI_LOW_LEVEL;
	SPI_initStruct.WordSize = 8;
	SPI_initStruct.Master = 1;
	SPI_initStruct.RXHFullIEn = 0;
	SPI_initStruct.TXEmptyIEn = 0;
	SPI_initStruct.TXCompleteIEn = 0;
	SPI_Init(SPI_PORT_W25X, &SPI_initStruct);
	SPI_Open(SPI_PORT_W25X);
	
	spi_flash->rdid = SPI_FLASH_ReadDeviceID();
	spi_flash->jedec_id = SPI_FLASH_ReadID();
	
	if(spi_flash->jedec_id == SPI_FLASH_ID_WD||spi_flash->jedec_id==SPI_FLASH_ID_BY)
	{
		spi_flash_initialised = 1;
		return 0;
	}
	
	return -1;
}

/**
 * Read spi_flash blocks
 *
 * @param[in]   spi_flash        the interface which should be read
 * @param[out]  data      pointer to the buffer which will store incoming data
 * @param[in]   blk_addr  spi_flash blk addr
 * @param[in]   blks      spi_flash blks
 * @param[in]   timeout   timeout in milisecond
 *
 * @return  0 : on success,  otherwise is error
 */
int32_t hal_spi_flash_blks_read(spi_flash_dev_t *spi_flash, uint8_t *data, uint32_t blk_addr,
                         uint32_t blks, uint32_t timeout)
{
	if (data == NULL || 0 == spi_flash_initialised)
		return -1;
	
	SPI_FLASH_BufferFastRead(data, blk_addr << 12, blks << 12);
	return 0;
}

/**
 * Write spi_flash blocks
 *
 * @param[in]  spi_flash        the interface which should be wrote
 * @param[in]  data      pointer to the buffer which will store incoming data
 * @param[in]  blk_addr  spi_flash blk addr
 * @param[in]  blks      spi_flash blks
 * @param[in]  timeout   timeout in milisecond
 *
 * @return  0 : on success,  otherwise is error
 */
int32_t hal_spi_flash_blks_write(spi_flash_dev_t *spi_flash, uint8_t *data, uint32_t blk_addr,
                          uint32_t blks, uint32_t timeout)
{
	uint32_t i;
	uint32_t flash_addr;
	if (data == NULL || 0 == spi_flash_initialised)
		return -1;
	flash_addr =  blk_addr << 12;
	for(i = 0; i < blks; i++)
	{
		SPI_FLASH_SectorErase(flash_addr + (i << 12)/*i * 4096*/);
	}
	
	SPI_FLASH_BufferWrite(data,  flash_addr, blks <<  12);
	return 0;
}

/**
 * Erase spi_flash blocks
 *
 * @param[in]  spi_flash              the interface which should be erased
 * @param[in]  blk_start_addr  spi_flash blocks start addr
 * @param[in]  blk_end_addr    spi_flash blocks end addr
 *
 * @return  0 : on success,  otherwise is error
 */
int32_t hal_spi_flash_erase(spi_flash_dev_t *spi_flash, uint32_t blk_start_addr, uint32_t blk_end_addr)
{
	uint16_t i;
	uint32_t flash_addr;
	if (0 == spi_flash_initialised)
		return -1;
	flash_addr =  blk_start_addr << 12;
	for(i = blk_start_addr; i < blk_end_addr; i++)
	{
		SPI_FLASH_SectorErase(flash_addr + (i << 12)/*i * 4096*/);
	}
	return 0;
}

/**
 * Get spi_flash state
 *
 * @param[in]   spi_flash    the interface which should be got state
 * @param[out]  stat  pointer to the buffer which will store incoming state data
 *
 * @return  0 : on success,  otherwise is error
 */
int32_t hal_spi_flash_stat_get(spi_flash_dev_t *spi_flash, hal_spi_flash_stat *stat)
{
	if(spi_flash_initialised)
		*stat = FLASH_STAT_TRANSFER;
	else
		*stat = FLASH_STAT_RESET;
	return 0;
}

/**
 * Get spi_flash info
 *
 * @param[in]   spi_flash    the interface which should be got info
 * @param[out]  stat  pointer to the buffer which will store incoming info data
 *
 * @return  0 : on success,  otherwise is error
 */
int32_t hal_spi_flash_info_get(spi_flash_dev_t *spi_flash, hal_spi_flash_info_t *info)
{
	info->blk_size = FLASH_SECTOR_SIZE;
	info->blk_nums = FLASH_SECTOR_COUNT;
	return 0;
}

/**
 * Deinitialises a spi_flash interface
 *
 * @param[in]  spi_flash  the interface which should be Deinitialised
 *
 * @return  0 : on success,  otherwise is error
 */
int32_t hal_spi_flash_finalize(spi_flash_dev_t *spi_flash)
{
	return 0;
}

#endif
